home *** CD-ROM | disk | FTP | other *** search
- /*
- File: StdWModM.cpp
-
- Contains: Implementation of DefaultWindowModule
-
- Owned by: Chris Linn
-
- Copyright: © 1994 - 1996 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- <1> 11/27/96 CSL first checked in
-
- To Do:
- */
-
- /*
-
- Some Theory of Operation
-
- In pure OpenDoc, i.e., no container application, this is the module that manages windows.
- It can assume that all windows are registered with OpenDoc except for dialog windows. Any
- window that is not an ODWindow is treated as a dialog window for purposes of layering,
- acivation, etc.. The only exception to this is windows that are not visible on screen
- (ie, windows positioned far offscreen). Off-screen windows are treated just like hidden
- windows--they are ignored entirely by this module.
-
- A container application that creates its own windows (that are not registered with OpenDoc)
- will need to write its own window handler module that makes windows behave properly. A
- custom window handler module will know about the application's windows, and it will be
- able to ask OpenDoc about part windows (i.e. floating or not), but it will need to assume
- that any window that was not created by the application and is not an ODWindow is therefore
- a dialog window created by a part.
-
- About window behavior in this module:
-
- Windows that are hidden are ignored, since the toolbox likes to move hidden windows around
- in the window list. When a window is shown, it is repositioned in the window list only if
- it is currently in the wrong layer (e.g. you don't want a floater to appear behind a
- document window).
-
- Dialogs appear above floaters, floaters appear above document (regular OpenDoc) windows.
- All floating windows are active when there is no visible dialog window. The top-most
- document window is active when there is no visible dialog window. Only the top-most dialog
- window is active when one or more dialog windows are visible.
-
- Note that parts may choose to implement a dialog window as an ODWindow. In this case,
- OpenDoc does not know that the window is a dialog, and treats it like a document window.
- It turns out that there are only two problems with doing this. First, such a window
- is presented behind floating windows. In practice, this isn't an issue because the
- root frame in the dialog window gets the selection focus, causing floating windows owned
- by other frames to be hidden. In a container application, however, it may still cause
- the dialog to appear behind the container's floating windows, if they are visible.
-
- The second problem is with nested dialogs. If the first dialog presented is not an
- ODWindow (such as part info), but a nested dialog is an ODWindow, the nested dialog will
- always appear behind the first dialog window. There is no workaround for this limitation
- at this time. Nested dialogs cannot be ODWindows.
-
- */
-
- /*
- * This file was generated by the SOM Compiler.
- * Generated using:
- * SOM incremental update: 2.33
- */
-
-
- #ifndef _STDWMODM_
- #include "StdWModM.h"
- #endif
-
- #ifndef __WINDOWS__
- #include <Windows.h>
- #endif
-
- #ifndef SOM_ODWindowState_xh
- #include "WinStat.xh"
- #endif
-
- #ifndef SOM_ODWindow_xh
- #include "Window.xh"
- #endif
-
- #ifndef SOM_ODWinIter_xh
- #include "WinIter.xh"
- #endif
-
- #ifndef _WINUTILM_
- #include "WinUtilM.h"
- #endif
-
- #ifndef _TEMPOBJ_
- #include "TempObj.h"
- #endif
-
-
- //-------------------------------------------------------------------------------------
- #pragma mark --- Private Classes ---
- //-------------------------------------------------------------------------------------
-
- //-------------------------------------------------------------------------------------
- #pragma mark WindowInfo
- //-------------------------------------------------------------------------------------
-
- class WindowInfo
- {
- public:
- WindowInfo() : window( kODNULL ), odWindow( kODNULL ), layer( 0 ) {};
-
- ODPlatformWindow window;
- TempODWindow odWindow;
- ODWindowLayer layer;
- };
-
-
- //-------------------------------------------------------------------------------------
- #pragma mark --- Static Utilities ---
- //-------------------------------------------------------------------------------------
-
- //-------------------------------------------------------------------------------------
- // IsWindowBehind
- //-------------------------------------------------------------------------------------
-
- static ODBoolean IsWindowBehind(
- WindowPtr testWindow,
- WindowPtr referenceWindow )
- {
- ODBoolean isWindowBehind = kODFalse;
-
- WindowPtr window = ::GetWindowList();
- while ( window != kODNULL )
- {
- if ( window == testWindow )
- break;
-
- if ( window == referenceWindow )
- {
- isWindowBehind = kODTrue;
- break;
- }
-
- window = ::GetNextWindow( window );
- }
-
- return isWindowBehind;
- }
-
-
- //-------------------------------------------------------------------------------------
- // IsWindowVisibleOnDesktop
- //
- // Returns true if the window's visible flag is set AND the window actually
- // intersects the desktop region. This allows us to ignore windows that
- // are hidden way offscreen, often in Small Negative Numbers Land. Cyberdog
- // has a few of these, natch.
- //-------------------------------------------------------------------------------------
-
- static ODBoolean IsWindowVisibleOnDesktop( WindowPtr window )
- {
- return ::IsWindowVisible( window )
- && ::RectInRgn( &(**((WindowPeek)window)->strucRgn).rgnBBox, GetGrayRgn());
- }
-
-
- //-------------------------------------------------------------------------------------
- // GetNextVisibleWindow
- //-------------------------------------------------------------------------------------
-
- static WindowPtr GetNextVisibleWindow( WindowPtr startingWindow )
- {
- WindowPtr nextVisibleWindow =
- ( startingWindow == kODNULL ? ::FrontWindow()
- : ::GetNextWindow( startingWindow ));
-
- while ( !( nextVisibleWindow == kODNULL
- || ::IsWindowVisibleOnDesktop( nextVisibleWindow )))
- {
- nextVisibleWindow = ::GetNextWindow( nextVisibleWindow );
- }
-
- return nextVisibleWindow;
- }
-
-
- //-------------------------------------------------------------------------------------
- // GetWindowLayer
- //-------------------------------------------------------------------------------------
-
- static ODWindowLayer GetWindowLayer( Environment* ev, ODWindow* odWindow )
- {
- return ( odWindow == kODNULL )
- ? kODWinLayerDialog : (( odWindow->IsFloating( ev ))
- ? kODWinLayerFloating
- : kODWinLayerDocument );
- }
-
-
- //-------------------------------------------------------------------------------------
- // IsLayerAbove
- //-------------------------------------------------------------------------------------
-
- static inline ODBoolean IsLayerAbove( ODWindowLayer testLayer, ODWindowLayer referenceLayer )
- {
- // Layers are numbered sequentially, so this test is easy.
- return testLayer < referenceLayer;
- }
-
-
- //-------------------------------------------------------------------------------------
- // IsFrontProcess
- //-------------------------------------------------------------------------------------
-
- static ODBoolean IsFrontProcess()
- {
- ProcessSerialNumber currentPSN;
- ProcessSerialNumber frontPSN;
- OSErr getFrontProcessResult;
- OSErr getCurrentProcessResult;
- ODBoolean isSameProcess = kODFalse;
-
- // Compare this process and the front process
- getFrontProcessResult = ::GetFrontProcess( &frontPSN );
- getCurrentProcessResult = ::GetCurrentProcess( ¤tPSN );
-
- if ( ( getFrontProcessResult == noErr )
- && ( getCurrentProcessResult == noErr ))
- ::SameProcess( &frontPSN, ¤tPSN, &isSameProcess );
-
- return isSameProcess;
- }
-
-
- //-------------------------------------------------------------------------------------
- // SetWindowActive
- //-------------------------------------------------------------------------------------
-
- static void SetWindowActive( WindowPtr window, ODBoolean activeState )
- {
- ::SetWindowHilite( window, ::IsFrontProcess() ? activeState : kODFalse );
- }
-
-
- //-------------------------------------------------------------------------------------
- #pragma mark --- Private Methods ---
- //-------------------------------------------------------------------------------------
-
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::GetFirstVisibleNonFloatingWindow
- //-------------------------------------------------------------------------------------
-
- ODPlatformWindow DefaultWindowModule::GetFirstVisibleNonFloatingWindow( Environment *ev)
- {
- // Return the top-most visible window in the window list that is not a floater.
- // The returned window may be either a dialog or a document window. A window that
- // is not an ODWindow is assumed to be a dialog. The window returned is the
- // active non-floating window, or kODNULL if there are no visible non-floaters.
-
- WindowPtr firstNonFloatingWindow = kODNULL;
- ODWindowIterator* iter = kODNULL; ODVolatile( iter );
-
- TRY
-
- ODWindow* odWindow = kODNULL;
- WindowPtr window = ::FrontWindow();
-
- iter = fWindowState->CreateWindowIterator( ev );
-
- while ( window != kODNULL && firstNonFloatingWindow == kODNULL )
- {
- for ( odWindow = iter->First( ev );
- iter->IsNotComplete( ev );
- odWindow = iter->Next( ev ))
- {
- if ( odWindow->GetPlatformWindow(ev) == window )
- {
- if ( !odWindow->IsFloating( ev ))
- {
- firstNonFloatingWindow = window;
- }
- break;
- }
- }
- if ( odWindow == kODNULL ) // i.e., this one is not an ODWindow
- firstNonFloatingWindow = window;
-
- window = ::GetNextVisibleWindow( window );
- }
- ODDeleteObject( iter );
-
- CATCH_ALL
-
- firstNonFloatingWindow = kODNULL;
- ODDeleteObject( iter );
- RERAISE;
-
- ENDTRY
-
- return firstNonFloatingWindow;
- }
-
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::BringToFrontOfLayer
- //-------------------------------------------------------------------------------------
-
- void DefaultWindowModule::MoveWindowToLayer( Environment *ev,
- WindowInfo& input,
- ODBoolean toFront )
- {
- // Do two things: move a window into its layer if it is in another layer,
- // and move the window to the front of its layer if the toFront parameter
- // is true. For step one, if the window is behind its layer, the window
- // is moved to be behind the last window in its layer. If the window is
- // in a layer above where it should be, the window is simply moved to the
- // front of it's layer. The logic gets sort of tricky because you need
- // to deal with all the edge cases where there are no windows visible in
- // one or more of the layers involved.
-
- WindowPtr lastAboveLayer = kODNULL;
- WindowPtr lastBeforeNextLayer = kODNULL;
- ODWindowIterator* iter = kODNULL; ODVolatile( iter );
-
- TRY
-
- // Find the last visible window above the input window's layer
- // and the last visible window before the layer below the input window
- // (ie, if there are currently no visible windows in the input layer,
- // the last window before the next layer might be in the layer ABOVE
- // the input layer).
- ODWindow* searchODWindow = kODNULL;
- WindowPtr searchWindow = ::FrontWindow();
- iter = fWindowState->CreateWindowIterator( ev );
-
- while ( searchWindow != kODNULL )
- {
- searchODWindow = iter->First( ev );
- while ( iter->IsNotComplete( ev )
- && searchODWindow->GetPlatformWindow(ev) != searchWindow )
- {
- searchODWindow = iter->Next( ev );
- }
-
- // GetWindowLayer works even if searchODWindow is NULL
- // (the window is put in the dialog layer if no ODWindow can be found).
- ODWindowLayer searchLayer = ::GetWindowLayer( ev, searchODWindow );
- if ( ::IsLayerAbove( searchLayer,
- input.layer ))
- {
- lastAboveLayer = searchWindow;
- lastBeforeNextLayer = searchWindow;
- }
- else if ( searchLayer == input.layer )
- lastBeforeNextLayer = searchWindow;
- else
- break; // Optimization: stop searching after input's layer
-
- searchWindow = ::GetNextVisibleWindow( searchWindow );
- }
- ODDeleteObject( iter );
-
- // Move the window as needed.
- // This is the only code in the entire class that changes the z-ordering
- // of windows.
- if ( lastAboveLayer == kODNULL
- && ( toFront || lastBeforeNextLayer == kODNULL ))
- {
- // The window should appear in front of all others.
- ::BringToFront( input.window );
- }
- else if ( lastAboveLayer != kODNULL
- && ( toFront || !::IsWindowBehind( input.window, lastAboveLayer )))
- {
- // The window should appear at the front of its layer.
- ::SendBehind( input.window, lastAboveLayer );
- }
- else if ( lastBeforeNextLayer != kODNULL
- && ::IsWindowBehind( input.window, lastBeforeNextLayer ))
- {
- // The window has drifted back into a deeper layer
- // (thanks to the toolbox) and needs to be brought
- // forward to appear at the back of its layer.
- ::SendBehind( input.window, lastBeforeNextLayer );
- }
-
- CATCH_ALL
-
- ODDeleteObject( iter );
- RERAISE;
-
- ENDTRY
- }
-
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::SetFrontWindowsActiveState
- //-------------------------------------------------------------------------------------
-
- void DefaultWindowModule::SetFrontWindowsActiveState( Environment *ev,
- ODBoolean activeState)
- {
- WindowPtr firstNonFloater = this->GetFirstVisibleNonFloatingWindow( ev );
- WindowPtr window = ::FrontWindow();
-
- // Starting at the top, set the active state of all visible windows
- // down to the first non floating window
-
- while ( window != kODNULL )
- {
- TempODWindow odWindow =
- fWindowState->AcquireODWindow( ev, window );
-
- if ( odWindow == kODNULL )
- {
- // This window is not an ODWindow.
- // Assume it is a dialog and set the hilite.
- ::SetWindowActive( window, activeState );
- }
- else if ( activeState )
- odWindow->Activate( ev );
- else
- odWindow->Deactivate( ev );
-
- // Time to stop?
- if ( window == firstNonFloater )
- break;
-
- window = ::GetNextVisibleWindow( window );
- }
- }
-
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::Normalize
- //-------------------------------------------------------------------------------------
-
- void DefaultWindowModule::Normalize( Environment *ev,
- ODPlatformWindow inputWindow,
- WindowInfo& inputInfo,
- WindowInfo& activeInfo)
- {
- // Fill in the inputInfo and activeInfo structures, based on the input the
- // inputWindow and the active window, respectively.
-
- inputInfo.window = inputWindow;
- if ( inputWindow != kODNULL )
- {
- inputInfo.odWindow = fWindowState->AcquireODWindow( ev, inputWindow );
- inputInfo.layer = ::GetWindowLayer( ev, inputInfo.odWindow );
- }
-
- // It's important that we determine which window is "active" based solely
- // on position in the window list as opposed to the hilite state or the
- // value returned by ODWindow::IsActive(). The position technique is
- // independent of whether the process is in the foreground or background,
- // which is what we need. Thus, in this context, the "active" window means
- // the window which is active when the process is in the foreground.
- activeInfo.window = this->GetFirstVisibleNonFloatingWindow( ev );
- if ( activeInfo.window != kODNULL )
- {
- activeInfo.odWindow = fWindowState->AcquireODWindow( ev, activeInfo.window );
- activeInfo.layer = ::GetWindowLayer( ev, activeInfo.odWindow );
- }
- }
-
-
- //-------------------------------------------------------------------------------------
- #pragma mark --- Public Methods ---
- //-------------------------------------------------------------------------------------
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::InitDefaultWindowModule
- //-------------------------------------------------------------------------------------
-
- void DefaultWindowModule::InitDefaultWindowModule( Environment *ev,
- ODWindowState* windowState)
- {
- if ( windowState == kODNULL )
- THROW( kODErrIllegalNullInput );
-
- fWindowState = windowState;
- }
-
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::ShowWindow
- //-------------------------------------------------------------------------------------
-
- void DefaultWindowModule::ShowWindow( Environment *ev,
- ODPlatformWindow window,
- ODWindowLayer layer)
- {
- // The window being shown is not assumed to be hidden beforehand. This is
- // because this method is used when an already visible window is added
- // (ie, registered with OpenDoc) to force the window into the right layer.
-
- // Note that we don't keep a list of windows and layers.
- // Instead, we rely on ODWindowState to keep track window layers. This
- // is a simple technique that works only because this module is running
- // in "pure" OpenDoc, with no container app windows. In a container app,
- // this module would need to add this window and it's layer to an internal
- // list of all the windows in the process, and then use that list during
- // other method calls.
-
- WindowInfo input;
- WindowInfo active;
- this->Normalize( ev, window, input, active );
-
- // Figure out if the window needs to be active.
- // This is a little tricky. Floaters are active if no
- // dialogs are active. Others in front (document & dialog windows)
- // need to be Selected so that the currently active window
- // gets deactivated.
- if ( input.layer == kODWinLayerFloating )
- {
- // Make sure the window will appear in the right layer.
- this->MoveWindowToLayer( ev, input, kODFalse /* !BringToFront */ );
- if ( active.window == kODNULL
- || active.layer == kODWinLayerDocument )
- {
- input.odWindow->Activate( ev );
- }
- }
- else if ( input.layer == kODWinLayerDialog
- || active.window == kODNULL
- || !::IsWindowBehind( input.window, active.window ))
- {
- // Select windows that should be active (front non-floaters)
- if ( input.odWindow != kODNULL )
- input.odWindow->Select( ev );
- else
- this->SelectWindow( ev, input.window );
- }
- else
- {
- // Make sure the window will appear in the right layer.
- this->MoveWindowToLayer( ev, input, kODFalse /* !BringToFront */ );
- }
-
- // Finally, show the window.
- ::ShowHide( input.window, kODTrue );
- }
-
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::HideWindow
- //-------------------------------------------------------------------------------------
-
- void DefaultWindowModule::HideWindow( Environment *ev,
- ODPlatformWindow window)
- {
- WindowInfo input;
- WindowInfo active;
- this->Normalize( ev, window, input, active );
-
- // Deactivate it in case it's active
- // (It's safe and fast to call Deactivate() on an inactive window)
- if ( input.odWindow != kODNULL )
- input.odWindow->Deactivate( ev );
-
- // Hide the window
- ::ShowHide( input.window, kODFalse );
-
- if ( input.window == active.window )
- {
- // We just hid the active window, so now we need to activate the
- // new set of front windows (ie, floaters too, if needed)
- fWindowState->ActivateFrontWindows( ev );
- }
- }
-
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::SelectWindow
- //-------------------------------------------------------------------------------------
-
- void DefaultWindowModule::SelectWindow( Environment *ev,
- ODPlatformWindow window)
- {
- // The window being selected is not assumed to be visible. This allows
- // a window to be activated just before being shown. See the Show() method.
-
- ODBoolean shouldActivate = kODFalse;
- WindowInfo input;
- WindowInfo active;
- this->Normalize( ev, window, input, active );
-
- // If the window displaces the active window, deactivate the active window.
- // Also figure out if the window should be active after bringing it to the
- // front of its layer.
- if ( input.window != active.window )
- {
- switch( input.layer )
- {
- case kODWinLayerDocument:
- // If no dialogs are visible, deactivate the front doc window
- if ( active.window == kODNULL
- || active.layer == kODWinLayerDocument )
- {
- shouldActivate = kODTrue;
- if ( active.odWindow != kODNULL )
- active.odWindow->Deactivate( ev );
- else if ( active.window != kODNULL )
- ::SetWindowActive( active.window, kODFalse );
- }
- break;
-
- case kODWinLayerFloating:
- // If no dialogs are visible, mark the window to be activated
- if ( active.window == kODNULL
- || active.layer == kODWinLayerDocument )
- {
- shouldActivate = kODTrue;
- }
- break;
-
- case kODWinLayerDialog:
- // Always activate a dialog.
- shouldActivate = kODTrue;
- fWindowState->DeactivateFrontWindows( ev );
- break;
-
- default:
- break;
- }
- }
-
- // Bring the window to the front of its layer.
- this->MoveWindowToLayer( ev, input, kODTrue /* BringToFront */ );
-
- // Finally, if it should be active, activate it.
- if ( shouldActivate )
- {
- if ( input.odWindow != kODNULL )
- input.odWindow->Activate( ev );
- else
- ::SetWindowActive( input.window, kODTrue );
- }
- }
-
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::DeactivateFrontWindows
- //-------------------------------------------------------------------------------------
-
- void DefaultWindowModule::DeactivateFrontWindows( Environment *ev)
- {
- this->SetFrontWindowsActiveState( ev, kODFalse );
- }
-
-
- //-------------------------------------------------------------------------------------
- // DefaultWindowModule::ActivateFrontWindows
- //-------------------------------------------------------------------------------------
-
- void DefaultWindowModule::ActivateFrontWindows( Environment *ev)
- {
- this->SetFrontWindowsActiveState( ev, kODTrue );
- }
-